package affd.logic;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;


import affd.logic.Token.Type;

public class Extractor {
	
	private HfstStarter hfstStarter;
	private boolean isDeleteListUsed = false;
	private HashSet<String> deleteList;
	
	public Extractor() {
		// Empty
	}

	/**
	 * This method sets the HfstStarter for the extractor.
	 */
	public void setHfstStarter(HfstStarter h) {
		hfstStarter = h;
	}

	/**
	 * With this method Extractor is set the the deleteList if one is in use.
	 * @param deleteList2 Contains the names to delete
	 */
	public void setDeleteListSettings(HashSet<String> deleteList2) {
		if (deleteList2 != null) {
			this.deleteList = deleteList2;
			this.isDeleteListUsed = true;			
		} else {
			this.isDeleteListUsed = false;
		}
	}

	
	/** Cutting off the most evident suffixes - ssa, ssä, lla a.o. 
	 * 
	 * @param word The string whose suffix will be cut off.
	 * @return The string without a suffix.
	 */
    private String deleteSuffix(String word) {

    	String[] suffixes = new String[8];
    	suffixes[0] = "ssa";
    	suffixes[1] = "ss\u00E4";
    	suffixes[2] = "lla";
    	suffixes[3] = "ll\u00E4";
    	suffixes[4] = "sta";
    	suffixes[5] = "st\u00E4";
    	suffixes[6] = "lta";
    	suffixes[7] = "lt\u00E4";

    	if (word.endsWith(suffixes[0]) || word.endsWith(suffixes[1]) || 
    		word.endsWith(suffixes[2]) || word.endsWith(suffixes[3]) || 
    		word.endsWith(suffixes[4]) || word.endsWith(suffixes[5]) || 
    		word.endsWith(suffixes[6]) || word.endsWith(suffixes[7])) {

    		word = word.substring(0, word.length() - 3); 
    	}
    	return word;
    }

    /**
     * Checks if the given two keys are close enough each others to be the same keys.
     * 
     * @param word1 The first word which is compared
     * @param word2 The second word which is compared
     * @return Integer value which indicates how different the words are from each others.
     * Value 0 means that the keys are most probably the same
     */
    private int areKeysSame(String word1, String word2) {
    	if (word1.length() > 3 && word2.length() > 3) {
    	    int minLen = Math.min(word1.length(), word2.length());
    	    word1 = word1.substring(0, minLen - 3);
    	    word2 = word2.substring(0, minLen - 3);
    	}
    	return word1.compareTo(word2);
    }
    
    /**
     * Finds from the given key set if it contains already a key which could be the same
     * key than the given word.
     * 
     * @param word The word for which a similar key is searched for
     * @param keys The Set of already existing keys
     * @return The key which could be the same key as the given word, if one exists.
     * Otherwise null.
     */
    private String getNearestKey(String word, Set<String> keys) {   	
    	Iterator<String> iterator = keys.iterator();
    	String currentKey;
    	while (iterator.hasNext()) {
    		currentKey = iterator.next();
    		if (areKeysSame(currentKey, word) == 0)
    			return currentKey;
    	}
    	return null;
    }
	
	/**  
	 * ExtractPossibleNames goes through the tokens and extracts words which are probably
	 * to be names.
	 * 
	 * @param sentences Sentences of the text.
	 * @return The hashmap containing a possible occurances of the names.
	 */
	public HashMap<String, ArrayList<Token>> extractPossibleNames(ArrayList<Sentence> sentences) throws java.io.IOException {
		
		HashMap<String, ArrayList<Token>> nameCandidates = 
			new HashMap<String, ArrayList<Token>>();
		/* name candidates which is not recognized by Hfst */
		HashMap<String, ArrayList<Token>> unknownNameCandidates = 
			new HashMap<String, ArrayList<Token>>();	
		
		ArrayList<String> basicForms;
		//ArrayList<String> unfoundBasicForms;
		ArrayList<Token> occurances;
		String nearestKey;
		
		ArrayList<Token> tokens;
		for (Sentence sentence : sentences) {
			tokens = sentence.getTokens();
			for (Token token : tokens) {
				
				if (token.getType() == Type.WORD && 
                        (Character.isUpperCase(token.getContent().charAt(0)) || isDeleteListUsed)) {
					
					basicForms = hfstStarter.getBasicForms(token.getContent());
					
                    if (hfstStarter.wasPronoun(token.getContent()))
                    	continue;
						
                    if (basicForms == null)
                    	continue;

                    boolean isInDeleteList = false;

					// checking whether the current word's basic form is in the delete list
                    if (isDeleteListUsed) {
                          for (String basicForm : basicForms) {
                        	  if (deleteList.contains(basicForm)) {
                        		  isInDeleteList = true;
//                                  System.out.println("Word is in the delete list");
                        		  break;
                        	  }
                          }
                    }

                    if (Character.isUpperCase(token.getContent().charAt(0)) || isInDeleteList) {

						/* The word wasn't recognized by Hfst */
                    	if (basicForms.size() == 0) {
                    		nearestKey = getNearestKey(deleteSuffix(token.getContent()), unknownNameCandidates.keySet());					
                    		if (nearestKey == null)
                    			nearestKey = deleteSuffix(token.getContent());
                    		occurances = unknownNameCandidates.get(nearestKey);
							
                    		if (occurances == null) {
	                    		occurances = new ArrayList<Token>();
	                    		occurances.add(token);
	                    		unknownNameCandidates.put(nearestKey, occurances);
                    		}
                    		else if (!token.equals(occurances.get(occurances.size() - 1))) {
                    			occurances.add(token);
                    		}
                    	}
						
                    	/* The word is in the dictionary of Hfst */
                    	for (String basicForm : basicForms) {
	
                    		occurances = nameCandidates.get(basicForm);
                    		if (occurances == null) {
                    			occurances = new ArrayList<Token>();
                    			occurances.add(token);
                    			nameCandidates.put(basicForm, occurances);
                    		}
                    		else if (!token.equals(occurances.get(occurances.size() - 1))) {
                    			occurances.add(token);
                    		}
                    	}
                    }					
				}
			}
		}
		
		/* Adding unrecognized keys to recognized keys */

		nameCandidates.putAll(unknownNameCandidates);

		return nameCandidates;				
	}
}
